package cn.seaboot.admin.user.service;

import cn.seaboot.admin.mvc.argument.ModifyArgument;
import cn.seaboot.admin.mvc.cache.CacheName;
import cn.seaboot.admin.mvc.Result;
import cn.seaboot.admin.mvc.valid.ValidationUtils;
import cn.seaboot.admin.user.bean.Role;
import cn.seaboot.admin.user.bean.User;
import cn.seaboot.admin.user.bean.UserDept;
import cn.seaboot.admin.user.bean.UserDetail;
import cn.seaboot.admin.user.context.AuthorityContext;
import cn.seaboot.admin.user.context.UserContext;
import cn.seaboot.admin.user.dao.UserDao;
import cn.seaboot.commons.core.ListUtils;
import cn.seaboot.commons.core.UUIDUtils;
import cn.seaboot.commons.exception.BizException;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;

/**
 * UserService
 *
 * @author Mr.css on 2018/7/18.
 */
@Service
public class UserService {

    /**
     * 防止嗅探所使用的用户ID
     */
    private static final String PROBE_USER_ID = "test";
    /**
     * 默认密码
     */
    private static final String DEF_PASSWORD = "test";
    /**
     * 默认用户组
     */
    private static final String DEF_DEPT = "0002";

    @Resource
    private UserDao userDao;

    @Resource(name = "securityUserContext")
    private UserContext userContext;

    @Resource(name = "securityAuthorityContext")
    private AuthorityContext securityContext;

    @Resource
    private RoleService roleService;

    @Resource
    private UserDeptService userDeptService;

    /**
     * 导入账号
     *
     * @param list 账号
     * @return affected rows
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public Result importUser(List<User> list) {
        for (User user : list) {
            this.register(user);
        }
        return Result.SUCCEED;
    }

    /**
     * 注册新的账号
     *
     * @param account  账号
     * @param password 密码
     * @return affected rows
     */
    public int register(String account, String password) {
        User user = new User();
        user.setAccount(account);
        user.setPassword(password);
        return this.register(user);
    }


    /**
     * 注册账号
     *
     * @param user 账号
     * @return affected rows
     */
    public int register(User user) {
        String account = user.getAccount();
        if (account == null) {
            throw new BizException("Account is undefined!");
        }
        if (this.queryByAccount(account) != null) {
            throw new BizException("Account already exist when register!");
        }
        String password = user.getPassword();
        if (password == null) {
            password = DEF_PASSWORD;
        }

        // 如果未指定用户名，则直接使用账号作为用户名
        if (user.getUsername() == null) {
            user.setUsername(account);
        }
        securityContext.generatePassword(user, password);

        // 唯一标识，业务通常会用
        user.setId(UUIDUtils.genUUID32());

        // 默认启用账号
        user.setDisabled(Boolean.FALSE);

        // 默认属于普通用户
        if (user.getDeptId() == null) {
            user.setDeptId(DEF_DEPT);
        }

        // 使用用户同属机构
        if (user.getOrgId() == null) {
            User u = userContext.getUser();
            u.setOrgId(u.getOrgId());
        }
        return userDao.insert(user);
    }

    /**
     * 查询用户详情
     *
     * @param account 账号
     * @return detail
     */

    public UserDetail queryUserDetail(String account) {
        User user = this.queryByAccount(account);
        UserDept dept = userDeptService.queryById(user.getDeptId());
        UserDetail userDetail = new UserDetail();
        userDetail.setUser(user);
        userDetail.setGroup(dept);

        Role role = roleService.queryById(dept.getRoleId());
        List<Role> roles = ListUtils.newArrayList(role);
        userDetail.setRoles(roles);
        return userDetail;
    }


    /**
     * 更新账户信息，包含一些基本信息
     *
     * @param user 用户
     * @return affected rows
     */
    public int update(User user) {
        return userDao.updateSampleById(user);
    }

    /**
     * 更换分组
     *
     * @param account 账号
     * @param deptId  部门ID
     * @return affected rows
     */
    @CacheEvict(cacheNames = CacheName.USER, key = "'sys_user_' + #account")
    public int updateGroupId(String account, String deptId) {
        User user = this.queryByAccount(account);
        if (user.getDeptId().equals(deptId)) {
            return Result.SUCCEED.getCode();
        } else {
            return userDao.updateGroupId(user.getId(), deptId);
        }
    }

    /**
     * 启用
     *
     * @param account 账号
     * @return affected rows
     */
    @CacheEvict(cacheNames = CacheName.USER, key = "'sys_user_' + #account")
    public int turnOn(String account) {
        return userDao.updateDisabled(account, Boolean.FALSE);
    }

    /**
     * 禁用
     *
     * @param account 账号
     * @return affected rows
     */
    @CacheEvict(cacheNames = CacheName.USER, key = "'sys_user_' + #account")
    public int turnOff(String account) {
        return userDao.updateDisabled(account, Boolean.TRUE);
    }

    /**
     * 重置账号密码。
     * 为了避免嗅探攻击，不论账号是否存在，都会进行密码重置操作
     *
     * @param account 账号
     * @return affected rows
     */

    public int resetPassword(String account) {
        User user = userDao.selectFullByAccount(account);
        if (user == null) {
            user = new User();
            user.setId(PROBE_USER_ID);
        }
        securityContext.generatePassword(user, DEF_PASSWORD);
        return userDao.updatePassword(user.getId(), user.getPassword(), user.getPasswordSalt());
    }


    /**
     * 修改密码
     *
     * @param oldPassword 旧的密码
     * @param newPassword 新的密码
     * @return affected rows
     */
    public int changePassword(String oldPassword, String newPassword) {
        User user = userContext.getUser();
        user = userDao.selectFullByAccount(user.getAccount());
        securityContext.matchPassword(user, oldPassword);
        securityContext.generatePassword(user, newPassword);
        return userDao.updatePassword(user.getId(), user.getPassword(), user.getPasswordSalt());
    }

    /**
     * 快捷更新
     *
     * 像是用户信息这些内容，可以输入之后，立即保存的方式做，这时候可以使用这个接口。
     *
     * @param arg 字段信息
     * @return affected rows
     */
    public int updateFieldById(ModifyArgument arg){
        ValidationUtils.validArgument(arg, User.class);
        return userDao.updateFieldById(arg);
    }

    /**
     * 通过账号查用户
     *
     * @param account 账号
     * @return pojo
     */
    @Cacheable(cacheNames = CacheName.USER, key = "'sys_user_' + #account")
    public User queryByAccount(String account) {
        User user = userDao.selectByAccount(account);
        // 以目前的代码来说，下面两行代码没什么实际效果，但是有存在的意义，别删
        user.setPassword(null);
        user.setPasswordSalt(null);
        return user;
    }

    /**
     * 查询列表
     *
     * @param params 参数
     * @return list
     */
    public List<User> queryList(Map<String, Object> params) {
        return userDao.selectList(params);
    }


    // authority ------------------------------------------------------------------------------------------

    /**
     * 查询列表
     *
     * @param params 参数
     * @return list
     */
    public List<User> queryFully(Map<String, Object> params) {
        params.put("ordId", userContext.getOrgId());
        return userDao.selectFully(params);
    }

    /**
     * 查询当前用户的直属上级
     *
     * @return user-list
     */
    public List<User> queryAbove() {
        return userDao.selectAbove(userContext.getOrgId(), userContext.getUserDept().getPath());
    }

    /**
     * 查询当前用户的平级和下级
     *
     * @return user-list
     */
    public List<User> queryHorizon() {
        return userDao.selectHorizon(userContext.getOrgId(), userContext.getUserDept().getPath());
    }

    /**
     * 查询当前用户的直属下级
     *
     * @return user-list
     */
    public List<User> queryBelow() {
        return userDao.selectBelow(userContext.getOrgId(), userContext.getUserDept().getPath());
    }
}
